/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grex.fitnessfunctions.ErrorFunctions;

import grex.IPredictiveModel;
import grex.Environment;

import grex.GP;
import grex.Options;
import grex.Prediction;
import grex.PredictionContainer;
import grex.fitnessfunctions.Classification.ACCFitness;
import grex.fitnessfunctions.Classification.AUCFitness;
import grex.fitnessfunctions.Classification.BRIFitness;
import grex.fitnessfunctions.Classification.BrevityFitness;
import grex.fitnessfunctions.Classification.EntropyFitness;
import grex.fitnessfunctions.Classification.GiniFitness;
import grex.fitnessfunctions.Regression.CORR_Fitness;
import grex.fitnessfunctions.Regression.MAE_Fitness;
import grex.fitnessfunctions.Regression.MAPE_Fitness;
import grex.fitnessfunctions.Regression.MUAPE_Fitness;
import grex.fitnessfunctions.Regression.RMSE_Fitness;
import grex.genes.GeneException;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Hashtable;

/**
 *
 * @author RIK
 */
public class ErrorCalculator implements Serializable{

    private List<IErrorFunction> regression = new ArrayList<IErrorFunction>();
    private List<IErrorFunction> classification = new ArrayList<IErrorFunction>();
    private IErrorFunction ACC = new ACCFitness();
    private IErrorFunction AUC = new AUCFitness();
    private IErrorFunction BRI = new BRIFitness();
    private IErrorFunction BRE = new BrevityFitness();
    private IErrorFunction MAE = new MAE_Fitness();
    private IErrorFunction C_MAE = new MAE_Fitness("C-MAE", 0.0);
    private IErrorFunction C_RMSE = new RMSE_Fitness("C-RMSE",0.0);
    private IErrorFunction C_MAPE = new MAPE_Fitness("C-MAPE",0.0);
    private IErrorFunction C_MUAPE = new MUAPE_Fitness("C-MUAPE",0.0);
    private IErrorFunction C_CORR = new CORR_Fitness("C-CORR",0.0);
    private IErrorFunction C_INST = new ErrorInstances("C-INST",0.0);
    private IErrorFunction C_ZERO = new ErrorZeroValues("C-ZERO",0.0);
    private IErrorFunction C_AVG = new ErrorAverage("C-AVG",0.0);
    private IErrorFunction RMSE = new RMSE_Fitness();
    private IErrorFunction MAPE = new MAPE_Fitness();
    private IErrorFunction MUAPE = new MUAPE_Fitness();
    private IErrorFunction LENGTH = new ErrorLength();
    private IErrorFunction CORR = new CORR_Fitness();
    private IErrorFunction INST = new ErrorInstances();
    private IErrorFunction ZERO = new ErrorZeroValues();
    private IErrorFunction AVG = new ErrorAverage();
    private int PROBLEM_TYPE = Options.CLASSIFICATION;
//    private NumberFormat nf = NumberFormat.getInstance();
    DecimalFormat percent = new DecimalFormat("#0.00");
    public final String TRAININGSET = "TRAIN", TESTSET="TEST";
    private boolean hasValidation;
    private Hashtable<String, IErrorFunction> IErrorFunctions = new Hashtable<String, IErrorFunction>();
    
        public ErrorCalculator(Environment env) {
        PROBLEM_TYPE=env.getOptions().getPROBLEM_TYPE();
        hasValidation = env.hasValidationSet();
        classification.add(ACC);
        classification.add(AUC);
        classification.add(BRI);
        classification.add(BRE);
        classification.add(LENGTH);
        classification.add(INST);
   /*     classification.add(new EntropyFitness());
        classification.add(new GiniFitness());
        classification.add(LENGTH);
        classification.add(new MeasureMaxProb());
        classification.add(new MeasureMinProb());
        classification.add(new MeasureAvgProb());
        classification.add(new MeasureAvgProbStd());
        classification.add(new MeasureAvgPredProb());
        classification.add(new MeasureInstOverAvgPredProb());
        classification.add(new MeasureLeafInstStd());
        classification.add(new MeasureNrOfLeaves());        */
        regression.add(C_MAE);
        regression.add(C_RMSE);
        regression.add(C_MAPE);
        regression.add(C_MUAPE);
        regression.add(C_CORR);
        regression.add(C_INST);
        regression.add(C_AVG);
        regression.add(C_ZERO);        
        regression.add(MAE);
        regression.add(RMSE);
        regression.add(MAPE);
        regression.add(MUAPE);
        regression.add(CORR);
        regression.add(INST);
        regression.add(AVG);
        regression.add(ZERO);        
        regression.add(LENGTH);
        for (IErrorFunction f : classification) {
            IErrorFunctions.put(f.getName(), f);
        }
        for (IErrorFunction f : regression) {
            IErrorFunctions.put(f.getName(), f);
        }


    }
    
    
    public ErrorCalculator(int problemType) {
        PROBLEM_TYPE=problemType;
        classification.add(ACC);
        classification.add(AUC);
        classification.add(BRI);
        classification.add(BRE);
        classification.add(LENGTH);
   /*     classification.add(new EntropyFitness());
        classification.add(new GiniFitness());
        
        classification.add(new MeasureMaxProb());
        classification.add(new MeasureMinProb());
        classification.add(new MeasureAvgProb());
        classification.add(new MeasureAvgProbStd());
        classification.add(new MeasureAvgPredProb());
        classification.add(new MeasureInstOverAvgPredProb());
        classification.add(new MeasureLeafInstStd());
        classification.add(new MeasureNrOfLeaves());       */ 
        regression.add(C_MAE);
        regression.add(C_RMSE);
        regression.add(C_MAPE);
        regression.add(C_MUAPE);
        regression.add(C_CORR);
        regression.add(C_INST);
        regression.add(C_AVG);
        regression.add(C_ZERO);        
        regression.add(MAE);
        regression.add(RMSE);
        regression.add(MAPE);
        regression.add(MUAPE);
        regression.add(CORR);
        regression.add(INST);
        regression.add(AVG);
        regression.add(ZERO);        
        regression.add(LENGTH);
        for (IErrorFunction f : classification) {
            IErrorFunctions.put(f.getName(), f);
        }
        for (IErrorFunction f : regression) {
            IErrorFunctions.put(f.getName(), f);
        }


    }
    public void setProblemType(int problemType){
        PROBLEM_TYPE = problemType;
    }

    public double calcError(IPredictiveModel model, String dataSetName, String functionName) throws GeneException {
        IErrorFunction f = getFunction(functionName);
        return f.calcError(model, getPredictionContainer(model, dataSetName));
    }
    //Calclualtes error for a prediction container

    public double calcError(PredictionContainer pc, String functionName) throws GeneException {
        IErrorFunction f = getFunction(functionName);
        return f.calcError(pc);
    }

    public double calcError(List<Prediction> predictions, String functionName) throws GeneException {
        IErrorFunction f = getFunction(functionName);
        return f.calcError(castToPredictionContainer(predictions));
    }

    public IErrorFunction getIErrorFunction(String name) {
        return IErrorFunctions.get(name);
    }

    public List<IErrorFunction> getRegressionFunctions() {
        return regression;
    }

    public List<IErrorFunction> getClassificationFunctions() {
        return classification;
    }

    public String getClassificationStatisitcs(GP gp) throws GeneException {
        return getStatisitcs(gp, classification);
    }

    public String getRegressionStatisitcs(GP gp) throws GeneException {
        return getStatisitcs(gp, regression);
    }
    
    public String getStatistics(GP gp) throws GeneException{
        if(PROBLEM_TYPE == Options.CLASSIFICATION)
            return getStatisitcs(gp, classification);
        else
            return getStatisitcs(gp, regression);
    }
    

  
    public String getStatistics(ArrayList<GP> models) throws GeneException{
        return getStatistics(castToPredictiveModelsList(models));
    }
    
    public  String getStatistics(List<IPredictiveModel> models) throws GeneException{
        if(PROBLEM_TYPE == Options.CLASSIFICATION)
            return getStatisitcs(models, classification);
        else
            return getStatisitcs(models, regression);
    }
    public String getMeanStatistics(ArrayList<GP> models) throws GeneException{
        return getMeanStatistics(castToPredictiveModelsList(models));
    }
    
    public String getMeanStatistics(List<IPredictiveModel> models) throws GeneException{
        if(PROBLEM_TYPE == Options.CLASSIFICATION)
            return getMeanStatisitcs(models, classification);
        else
            return getMeanStatisitcs(models, regression);
    }

    private String getStatisitcs(GP gp, List<IErrorFunction> functions) throws GeneException {
        String s = "";
        for (IErrorFunction f : functions) {
            s +=  percent.format(f.calcError(gp, gp.getPcTrain())) + "\t";
            if(gp.getPcVal().values().size()>0)
                s +=  percent.format(f.calcError(gp, gp.getPcVal())) + "\t";
            s +=  percent.format(f.calcError(gp, gp.getPcTest())) + "\t";
        }
        return s;
    }
    
    public String getStatisitcs(IPredictiveModel model) throws GeneException {
        String s = "";
        List<IErrorFunction> functions;
        if(PROBLEM_TYPE==Options.CLASSIFICATION)
            functions = classification;
        else
            functions= regression;
        for (IErrorFunction f : functions) {
            s +=  percent.format(f.calcError(model,model.getPcTrain())) + "\t";
            if(model.getPcVal().values().size()>0)
                s +=  percent.format(f.calcError(model,model.getPcVal())) + "\t";
            s +=  percent.format(f.calcError(model,model.getPcTest())) + "\t";
        }
        return s;
    }

    private String getStatisitcs(List<IPredictiveModel> models, List<IErrorFunction> functions) throws GeneException {
        String s = "", header = "";
        int index = 1;
        for (IPredictiveModel model : models) {
            s += "#" + index + ":\t";
            for (IErrorFunction f : functions) {               
                s += percent.format(f.calcError(model, model.getPcTrain())) + "\t";
                if(hasValidation)
                    s += percent.format(f.calcError(model, model.getPcVal())) + "\t";
                s+=  percent.format(f.calcError(model, model.getPcTest())) + "\t";
            }
            s += "\n";
            index++;
        }
        s += "Mean:\t";
        for (IErrorFunction f : functions) {
            header += "TRN_" + f.getName() + "\t";
            if(hasValidation)
                header += "VAL_" + f.getName() + "\t";
            header += "TST_" + f.getName() + "\t";
            s += percent.format(calcMeanError(models,f, IErrorFunction.TRAIN)) + "\t";
            if(hasValidation)
                s += percent.format(calcMeanError(models,f, IErrorFunction.VAL)) + "\t";
            s+=percent.format(calcMeanError(models,f, IErrorFunction.TEST)) + "\t";
        }
        return header+"\n"+s;

    }
    
        private String statisticsAsRowHeaders="";
        
      public String getStatisitcsAsRow(List<IPredictiveModel> models) throws GeneException {

        String s = "";
        int index = 1;
                List<IErrorFunction> functions;
        if(PROBLEM_TYPE==Options.CLASSIFICATION)
            functions = classification;
        else
            functions= regression;
        
        
        for (IPredictiveModel model : models) {
            for (IErrorFunction f : functions) {  
                statisticsAsRowHeaders += model.getName() + "_TRN_" + f.getName() + "\t";
                if(hasValidation)
                    statisticsAsRowHeaders += model.getName() + "_VAL_" + f.getName() + "\t";
                statisticsAsRowHeaders += model.getName() + "_TST_" + f.getName() + "\t";
                s += percent.format(f.calcError(model, model.getPcTrain())) + "\t";
                if(hasValidation)
                    s+=percent.format(f.calcError(model, model.getPcVal())) + "\t";
                s+=percent.format(f.calcError(model, model.getPcTest())) + "\t";
            }
            
            index++;
        }
        return s;

    }
        
     public String getStatisitcsAsRowHeaders(){
        return "Dataset\t" + statisticsAsRowHeaders;

    }
    
    public String getHeaders(){
        List<IErrorFunction> functions;    
        if(PROBLEM_TYPE == Options.CLASSIFICATION)                
            functions =  classification;
        else
            functions =  regression;
        String header="";
        for (IErrorFunction f : functions) {
            header += "TRN_" + f.getName() + "\t";
            if(hasValidation)
                header += "VAL_" + f.getName() + "\t";
            header += "TST_" + f.getName() + "\t";
        }
        return header+"\n";
    }
    
        private String getMeanStatisitcs(List<IPredictiveModel> gps, List<IErrorFunction> functions) throws GeneException {
 
        String s = "", header = "";
        s += "";
        for (IErrorFunction f : functions) {
            s += percent.format(calcMeanError(gps,f, IErrorFunction.TRAIN)) + "\t";
            if(hasValidation)
                s+= percent.format(calcMeanError(gps,f, IErrorFunction.VAL)) + "\t";
            s+= percent.format(calcMeanError(gps,f, IErrorFunction.TEST)) + "\t";
        }
        return s;
        }

    public IErrorFunction getFunction(String name) throws GeneException {
        IErrorFunction f = IErrorFunctions.get(name);
        if (f != null) {
            return f;
        }
        throw new GeneException("Error function " + name + " does not exist!");
    }
   /* public double calcError(IErrorFunction f, Environment e, List<Prediction> predictions){
        if(predictions ==null)
            return 0;
        PredictionContainer pc = new PredictionContainer(predictions.size());
            for(int i=0;i<predictions.size();i++){
                double[] index = new double[1];
                index[0]=i;
                pc.put(index,predictions.get(i));
            }
            return f.calcError(e,pc);
    }*/

    public double calcMeanError(List<IPredictiveModel> models,IErrorFunction f,String dataset) throws GeneException{
        double value=0;
        PredictionContainer pc;
                
        for(IPredictiveModel model:models){

            value += f.calcError(model,getPredictionContainer(model,dataset));
        }
        return value/models.size();
    }
    
    private PredictionContainer getPredictionContainer(IPredictiveModel model, String dataset) {
        if (dataset.equals(IErrorFunction.TRAIN)) {
            return model.getPcTrain();
        } else if (dataset.equals(IErrorFunction.TEST)){
            return model.getPcTest();
        } else{
            return model.getPcVal();
        }
    }
    private PredictionContainer castToPredictionContainer(List<Prediction> predictions){
        PredictionContainer pc = new PredictionContainer(predictions.size());
        for(Prediction p:predictions){
            pc.put(p.getInstance(), p);
        }
        return pc;
    }
    
    private List<IPredictiveModel> castToPredictiveModelsList(List<GP> gps){
        List<IPredictiveModel> pModels = new ArrayList<IPredictiveModel>(gps.size());
        pModels.addAll(gps);
        return pModels;
    }
}
